package org.xtext.example.mydsl.ui.popup.actions;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.mwe.utils.StandaloneSetup;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.xtext.example.mydsl.MyDslStandaloneSetup;
import org.xtext.example.mydsl.myDsl.Abs;
import org.xtext.example.mydsl.myDsl.App;
import org.xtext.example.mydsl.myDsl.Let;
import org.xtext.example.mydsl.myDsl.Nrif;
import org.xtext.example.mydsl.myDsl.Prog;
import org.xtext.example.mydsl.myDsl.T;
import org.xtext.example.mydsl.myDsl.T1;
import org.xtext.example.mydsl.myDsl.T2;
import org.xtext.example.mydsl.myDsl.T3;
import org.xtext.example.mydsl.myDsl.Tapp;
import org.xtext.example.mydsl.myDsl.Term;
import org.xtext.example.mydsl.myDsl.Trif;
import org.xtext.example.mydsl.myDsl.Uq;
import org.xtext.example.mydsl.myDsl.Var;
import org.xtext.example.mydsl.ui.Controller;


import com.google.inject.Injector;


public class RunClass implements IObjectActionDelegate {

    private Shell shell;
    private String fileName;
    private String workingPath;
    private String uriPrefix;

	
	/**
	 * Constructor for Action1.
	 */
	public RunClass() {
		super();
	}

	/**
	 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
	 */
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		shell = targetPart.getSite().getShell();
		
	}

	/**
	 * @see IActionDelegate#run(IAction)
	 */
	public void run(IAction action) {
		Term main =null;
		String lciTerm ="";
		String lciResult ="";
		String term ="";
		String type = "";
		try {
			 main = getProg().getMain();
			 lciTerm = termToStringLci(main);
			 lciResult = Controller.lciInterpreter(lciTerm);
			 term = termToString(main);
			 type =Controller.getTerm(term);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

		
		MessageDialog.openInformation(
			shell,
			"org.xtext.example.mydsl.ui",
			"Term: "+term+"\nType: "+type+"\nPure Lambda Term: "+lciTerm+"\nNormal From: "+lciResult);
		
		
	}

	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	public void selectionChanged(IAction action, ISelection selection) {
        fileName = toFileName(selection.toString());
        workingPath = toWorkingPath(selection.toString());
        uriPrefix = toUriPrefix(selection.toString());

	}
	
	
	/* SERVICE METHODS */
    private String toFileName(String name) {
            int idx = getLastSeparatorIndex(name);

            if (idx == -1)
                    return "";

            return name.substring(idx + 1, name.length() - 1);
    }

    private String toWorkingPath(String name) {
            IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
            String path = root.getLocation().toString() + "/";

            int idx = getLastSeparatorIndex(name);
            if (idx == -1)
                    return "";

            return path + name.substring(3, idx) + "/";
    }

    private String toUriPrefix(String name) {
            int idx = name.indexOf('/');
            if (idx == -1)
                    return "";

            return "platform:/resource"
                            + name.substring(idx, name.lastIndexOf('/')) + "/";
    }

    private int getLastSeparatorIndex(String name) {
            int idx = name.length() - 1;

            while (name.charAt(idx) != '/') {
                    idx--;
                    if (idx < 0)
                            return -1;
            }

            return idx;
    }

    /**
     * restituisce la radice dell'albero sintattico
     * @return la radice dell'albero sintattico
     * @throws IOException
     */
    private Prog getProg() throws IOException {
            StandaloneSetup standalone = new StandaloneSetup();
            standalone.setPlatformUri(workingPath);
            Injector injector = new MyDslStandaloneSetup()
                            .createInjectorAndDoEMFRegistration();
            XtextResourceSet resourceSet = injector
                            .getInstance(XtextResourceSet.class);
            resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL,
                            Boolean.TRUE);

            Resource resource = resourceSet.createResource(URI.createURI(uriPrefix
                            + fileName));
            InputStream in = new FileInputStream(workingPath + fileName);

            resource.load(in, resourceSet.getLoadOptions());

            if (resource.getContents().size() == 0) {
                    MessageDialog.openError(shell, "File not found",
                                    "The file is empty: no folder has been generated");
                    return null;
            }
            Prog model = (Prog) resource.getContents().get(0);

            if (model.getMain() == null) {
                    MessageDialog.openError(shell, "main empty", "The main is empty");
                    return null;
            }
            return model;
    }
    
    
    /**
     * restituisce la stringa che rappresentazione del lambda termine pulito dal
     * tipo
     * @param t termine
     * @return stringa che rappresentazione del lambda termine pulito dal tipo
     */
    private String termToStringLci(Term t) {

            if (t instanceof Var) {
                    return ((Var) t).getName();
            } else if (t instanceof Abs) {
                    return "(\\" + (((Abs) t).getParam()) + "."
                                    + termToStringLci(((Abs) t).getBody()) + ")";
            } else if (t instanceof App) {
                    return "(" + termToStringLci(((App) t).getT1()) + " "
                                    + termToStringLci(((App) t).getT2()) + ")";
            } else if (t instanceof Uq) {
                    return termToStringLci(((Uq) t).getTerm());
            } else if (t instanceof Tapp) {
                    return termToStringLci(((Tapp) t).getTerm());
            } else if (t instanceof Nrif) {
                    return termToStringLci(((Nrif) t).getNome().getTerm());
            } else if (t instanceof Let) {
                    return "((\\" + (((Let) t).getVar()) + "."
                                    + termToStringLci(((Let) t).getT2()) + ") "
                                    + termToStringLci(((Let) t).getT1()) + ")";
            }
            return "";
    }



    /**
     * restituisce la stringa che rappresenta il tipo
     * @param t : t il tipo
     * @return a stringa che rappresenta il tipo
     */
    private String typeToString(T t) {
            if (t instanceof T1) {
                    String p = ((T1) t).getP();
                    EList<T> c = ((T1) t).getC();
                    if (!c.isEmpty()) {
                            p += "->";
                            for (T a : c)
                                    p += typeToString(a);
                    }
                    return p;
            } else if (t instanceof T2) {
                    String p = "(" + typeToString(((T2) t).getP1()) + ")";
                    EList<T> c = ((T2) t).getC1();
                    if (!c.isEmpty()) {
                            p += "->";
                            for (T a : c)
                                    p += typeToString(a);
                    }
                    return p;

            } else if (t instanceof T3) {
                    return "forall " + (((T3) t).getVar()) + " "
                                    + typeToString(((T3) t).getT());

            } else if (t instanceof Trif) {
                    return typeToString(((Trif) t).getNome().getType());

            }
            return "";

    }

 
    /**
     * restituisce la stringa che rappresenta il termine Term t il termine
     *
     * @return la stringa che rappresenta il termine
     */
    private String termToString(Term t) {

            if (t instanceof Var) {
                    return ((Var) t).getName();

            } else if (t instanceof Abs) {
                    return "\\(" + (((Abs) t).getParam()) + ":"
                                    + typeToString(((Abs) t).getType()) + ")."
                                    + termToString(((Abs) t).getBody());
            } else if (t instanceof App) {
                    return "(" + termToString(((App) t).getT1()) + ")"
                                    + termToString(((App) t).getT2());
            } else if (t instanceof Uq) {
                    return "/\\" + (((Uq) t).getVar()) + "."
                                    + termToString(((Uq) t).getTerm());
            } else if (t instanceof Tapp) {
                    return "(" + termToString(((Tapp) t).getTerm()) + ")"
                                    + typeToString(((Tapp) t).getType());
            } else if (t instanceof Nrif) {
                    return termToString(((Nrif) t).getNome().getTerm());
            } else if (t instanceof Let) {
                    return "\\((" + (((Let) t).getVar()) + ":"
                                    + typeToString((((Let) t).getType())) + ")."
                                    + termToString(((Let) t).getT2()) + ")"
                                    + termToString(((Let) t).getT1());
            }
            return "";
    }

    
}
